package no.uio.ifi.cop.prover.rete;

import java.util.LinkedList;
import java.util.HashMap;
import java.util.HashSet;

import no.uio.ifi.cop.prover.RuleMatcher;

import no.uio.ifi.cop.common.Log;
import no.uio.ifi.cop.cl.Axiom;
import no.uio.ifi.cop.cl.Term;
/// import no.uio.ifi.cop.cl.Conjunction;
/// import no.uio.ifi.cop.cl.Predicate;
import no.uio.ifi.cop.cl.Substitution;

/**
 * ReteRuleMatchers hold possible matches to a rule/axiom.
 *
 * The MatchLists in the queue in combination with the pointer
 * to the last AlphaNode help us fetch actual matches from the
 * AlphaNodes where they are stored.
 *
 *
 * @version 1.0
 * @author bjarneh@ifi.uio.no
 */

public final class ReteRuleMatcher extends RuleMatcher implements Cloneable{

    // every element gets its own id, and we use this to generate them..
    private static int counter = 1;

    private int applied = 0;
    private boolean stopped = false;

    BetaNode lastBeta; // pointer to last BetaNode
    LinkedList<MatchList> queue; // list of possible matches

    HashSet<String> cache;


    public ReteRuleMatcher(Axiom ax){
        setAxiom( ax );
        setId( counter++ );
        queue = new LinkedList<MatchList>();
        cache = new HashSet<String>();
    }

    public void addBeta(BetaNode beta){
        lastBeta = beta;
    }

    // for HRete implementation only
    public void clearQueue(){
        queue.clear();
    }

    private Substitution join(LinkedList<Substitution> list){
        Substitution prev = new Substitution();
        for(Substitution subst : list){
            prev = subst.join( prev );
            if( prev == null){
                break;
            }
        }
        return prev;

    }


    // checkout rev:1942 to get the recursive version
    public Substitution peek(){

        MatchList current;
        LinkedList<Integer> tuple;
        LinkedList<Substitution> substList;
        Substitution combined = null;


        do{
            current = queue.peek();

            if( current == null ){ break; }

            tuple = current.tuple();
            substList = new LinkedList<Substitution>();
            lastBeta.fetch( tuple, substList );
            combined = join( substList );

            if( combined == null ){
                if( current.hasNext() && current.advance() ){
                    ; // we advance :-)
                }else{
                    queue.poll();// remove empty MatchList
                }
            }else{
                if( ! cache.contains( combined.toString() ) ){
                    break;
                }else{
                    //Log.info("already here: %s| %s\n", combined, getAxiom());
                    combined = null;
                    if( current.hasNext() && current.advance() ){
                        ; // we advance :-)
                    }else{
                        queue.poll();// remove empty MatchList
                    }
                }
            }

        }while(! stopped);

        if( combined != null ){
            Log.info("not here: %s | %s\n", combined, getAxiom());
        }

        return combined;

    }

    // short way of saying: peek + advance 
    public Substitution poll(){
        Substitution subst = peek();
        if( subst != null ){ cache.add(subst.toString()); }
        advance();
        return subst;
    }

    public boolean advance(){

        MatchList current = queue.peek();
        if( current == null ){ Log.fatal(" queue is empty "); }
        LinkedList<Integer> tuple = current.next();
        if( current.hasNext() ){
            current.advance();
        }else{
            queue.poll();
            if( queue.isEmpty() ){
                return false;
            }
        }
        return true;
    }

    // special case for HReteRulefinder...
    public boolean cacheAdvance(){
        Substitution subst = peek();
        if( subst == null ){ Log.fatal(" subst == null"); }
        cache.add( subst.toString() );
        return advance();
    }


    public boolean exhausted(){
        if( stopped ){ return true; }
        return ( peek() == null );
    }

    public void addMatchList(MatchList ml){
        queue.add( ml );
    }

    public String infoString(){
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("%s\n", getAxiom()));
        lastBeta.infoString( sb );
        return sb.toString();
    }

    public void stop(){
        stopped = true;
    }

    public String toString(){
        return String.format("@ReteRuleMatcher( %s )", getAxiom());
    }

    public void used(){ applied++; }
    public int applications(){ return applied; }

    /* for cloning purposes below this line */

    public void addAlpha(HashMap<String, AlphaNode> lookUp){

        LinkedList<AlphaNode> alphaNodes = new LinkedList<AlphaNode>();

        String tmpKey;
        AlphaNode tmpValue;

        for( Term t : getAxiom().getLHS() ){

            tmpKey   = t.toString();
            tmpValue = lookUp.get( tmpKey );

            if( tmpValue != null ){
                alphaNodes.addLast( tmpValue );
            }
        }

        lastBeta.addAlpha( alphaNodes );
    }

    // constructor for cloning purposes
    private ReteRuleMatcher(HashSet<String> c, LinkedList<MatchList> q, boolean stop, int ap){

        stopped = stop;
        queue = new LinkedList<MatchList>();

        cache = new HashSet<String>();
        
        for( String subst : c ){
            cache.add( subst );
        }

        for( MatchList matchlist : q ){
            queue.add( matchlist.clone() );
        }

        applied = ap;
    }


    public ReteRuleMatcher clone(){
        ReteRuleMatcher cp = new ReteRuleMatcher(cache, queue, stopped, applied);
        cp.setAxiom( getAxiom() );
        cp.setId( getId() );
        cp.addBeta( lastBeta.clone() );
        return cp;
    }
}
